/******************************************************************************************
 * Import
 ******************************************************************************************/

// module
import _ from 'lodash';

// project
import Time from '../basis/time';
import Http from '../net/http';
import Storage from '../cache/storage';
import {Config} from '../../../preset';

/******************************************************************************************
 * Export Class
 ******************************************************************************************/

/**
 * @see Move、Scroll中的absolute、relative结构：{
 *      point: {
 *          start: {
 *              x: int,
 *              y: int
 *          },
 *          end: {
 *              x: int,
 *              y: int
 *          },  
 *      },
 *      distance: {
 *          x: int,
 *          y: int
 *      }
 * }
 * @see Click中的absolute、relative结构：{
 *      x: int,
 *      y: int 
 * }
 */
export default class Collect {

    /**
     * 收集到达数据
     * 
     * @param {String} arrive 本次到达标识
     * @param {Object} device 设备数据
     * @param {Object} env 环境数据
     * @param {String} code 来源码（推广事件的唯一编码）
     */
    static arrive(arrive, device, env, code = '') {
        Http.get({
            url: Config.url.collect,
            data: {
                created: Time.get10(),
                device,
                env,
                code
            }
        });
    }

    /**
     * 收集错误数据
     * 
     * @param {String} arrive 本次到达标识
     * @param {String} category 出错的类型名，如：lang, func, 
     * @param {String} file 出错的文件名
     * @param {String} func 出错的方法名
     * @param {String} entity 出错的实体名（如：local为key，http.get为url）
     * @param {String} param 出错时的参数（如果有的话）
     * @param {Error} err 错误对象（如果有的话）
     */
    static error(arrive, category, file, func, entity, param, err) {
        Http.get({
            url: Config.url.collect,
            data: {
                created: Time.get10(),
                arrive,
                category,
                file, 
                func,
                entity,
                param,
                err
            }
        })
    }

    /**
     * 收集访问数据 
     * 
     * @param {String} arrive 本次到达标识
     * @param {String} page 页面名称
     * @param {Object} param 页面参数
     * @param {Int} effect 加载效率（毫秒）
     * @param {Int} entry 进入时间戳（毫秒）
     * @param {Int} leave 离开时间戳（毫秒）
     * @param {Int} duration 持续时长（毫秒）
     */
    static visit(arrive, page, param, effect, entry, leave, duration) {
        this._collect('visit', {
            created: Time.get10(),
            arrive,
            page,
            param,
            effect,
            entry,
            leave,
            duration
        });
    }

    /**
     * 收集页面滚动数据
     * 
     * @param {String} arrive 本次到达标识
     * @param {String} page 页面名称
     * @param {Object} absolute 绝对坐标系（相对于页面顶端）中的开始点、结束点、距离
     * @param {Object} relative 相对坐标系（相对于屏幕顶端）中的开始点、结束点、距离
     */
    static move(arrive, page, absolute, relative) {
        this._collect('move', {
            created: Time.get10(),
            arrive,
            page,
            absolute,
            relative
        });
    }

    /**
     * 收集组件滚动数据
     * 
     * @param {String} arrive 本次到达标识
     * @param {String} page 页面名称
     * @param {String} element 元素（组件）名称
     * @param {Int} direction 滚动方向，分为4个方向：0：从上向下，1：从下向上，2：从左向右，3：从右向左
     * @param {Object} absolute 绝对坐标系（相对于页面顶端）中的开始点、结束点、距离
     * @param {Object} relative 相对坐标系（相对于屏幕顶端）中的开始点、结束点、距离
     */
    static scroll(arrive, page, element, direction, absolute, relative) {
        this._collect('scroll', {
            created: Time.get10(),
            arrive,
            page,
            element,
            direction,
            absolute,
            relative
        });
    }

    /**
     * 
     * @param {String} arrive 本次到达标识
     * @param {String} page 页面名称
     * @param {String} element 元素（组件）名称
     * @param {String} button 被点击按钮的名称
     * @param {Object} absolute 绝对坐标系（相对于页面顶端）中的点击位置
     * @param {Object} relative 相对坐标系（相对于屏幕顶端）中的点击位置
     */
    static click(arrive, page, element, button, absolute, relative) {
        this._collect('click', {
            created: Time.get10(),
            arrive,
            page,
            element,
            button,
            absolute,
            relative
        });
    }

    /**
     * 为visit, move, scroll, click提供服务的，实际的使用缓存进行数据收集的方法
     * 
     * @param {String} type 需要收集类型
     * @param {Object} obj 需要收集的数据对象
     */
    static _collect(type, data) {

        // 获取缓存使用配置项
        const config = Config.collect[type];

        // 获取缓存、设置处理回调
        Storage.get({
            key: config.key,
            success(res) {

                // 转换res为json
                json = JSON.parse(res);

                // 当前值有效判断：
                // 1、json.maxTimestamp不是整数，或json.value不是数组，都说明此数据结构不正确，需要重构
                // 2、重构结构
                if (_.isInteger(json.maxTimestamp) || !_.isArray(json.value)) {
                    json = this._getNewCollectStorage(config.maxSecond);            
                }

                // 查看当前数据是否已满足上传要求，达到要求则刷新数据
                // 1、缓存时间已到达最大时间
                // 2、缓存数据量已到达最大数量
                if (
                    json.maxTimestamp < Time.get10() || 
                    _.isArray(json.value) && json.value.length > config.maxLength
                ) {
                    this._refresh(config, json.value);
                    return;             
                }

                // 如果未达到刷新要求，则在json.value中添加一条新数据，并重新入缓存
                json.value.push(data);
                Storage.set({
                    key: config.key,
                    data: json
                });
            }
        });
    }

    /**
     * 发送数据到服务端
     * 
     * @param {Object} config 此次收集时使用的配置对象
     * @param {Object} data 需要上传的对象
     */
    static _refresh(config, data) {

        // 发送到服务端，并在发送成功之后写一个新生成的空数据给缓存
        Http.get({
            url: Config.url.collect,
            data: data,
            success(res) {
                Storage.set({
                    key: config.key,
                    data: this._getNewCollectStorage(config.maxSecond)
                });
            }
        });
    }

    /**
     * 生成一个新的缓存结构
     * 
     * @param {Int} maxSecond 缓存最大秒数
     */
    static _getNewCollectStorage(maxSecond) {
        return {
            maxTimestamp: Time.get10() + maxSecond,
            value: []
        };
    }
}
